﻿using System.Collections.Generic;
using System.Net;

namespace ThingsGateway.Foundation
{
    public abstract class ReadWriteDevicesUdpDataHandleAdapter : UdpDataHandlingAdapter
    {
        /// <summary>
        /// TCP读写设备对象，当插件在被第一次加载时赋值
        /// </summary>
        protected ReadWriteDevicesUdpClientBase ReadWriteDevicesUdpClientBase { get; private set; }
        /// <summary>
        /// 当前读写请求对象，每次发送新数据会重新赋值
        /// </summary>
        protected ReadWriteRequest ReadWriteRequest { get; private set; }

        private IByteConverter byteConverter = new ByteConverter(EndianType.Big, DataFormat.DCBA);
        private ushort timeOut = 2000;
        private ushort wordLength = 1;

        /// <summary>
        /// 当前的数据变换机制（统一插件配置入口，实际会赋值到TCP读写设备对象）。<br />
        /// </summary>
        public IByteConverter ByteConverter
        {
            get
            {
                return byteConverter;
            }
            protected set
            {
                byteConverter = value;
                ReadWriteDevicesUdpClientBase?.SetValue(ReadWriteDevicesEx.ByteConverterProperty, value);
            }
        }
        public DataFormat DataFormat
        {
            get
            {
                return ByteConverter.DataFormat;
            }

            set
            {
                ByteConverter.DataFormat = value;
                ReadWriteDevicesUdpClientBase?.SetValue(ReadWriteDevicesEx.ByteConverterProperty, value);
            }
        }

        public override bool CanSendRequestInfo => true;

        public override bool CanSplicingSend => true;
        /// <summary>
        /// 读写超时时间（统一插件配置入口，实际会赋值到TCP读写设备对象）
        /// </summary>
        public ushort TimeOut
        {
            get
            {
                return timeOut;
            }
            set
            {
                timeOut = value;
                ReadWriteDevicesUdpClientBase?.SetValue(ReadWriteDevicesEx.TimeOutProperty, value);
            }
        }
        /// <summary>
        /// 两个字节表示的地址长度（统一插件配置入口，实际会赋值到TCP读写设备对象）<br />
        /// </summary>
        public ushort WordLength
        {
            get
            {
                return wordLength;
            }
            set
            {
                wordLength = value;
                ReadWriteDevicesUdpClientBase?.SetValue(ReadWriteDevicesEx.WordLengthProperty, value);
            }
        }

        /// <summary>
        /// 读写接口返回的实际消息对象，每次发送数据前会清空字节数组
        /// </summary>
        protected abstract NetMessageBase NetMessage { get; set; }

        public override void OnLoaded(IUdpSession client)
        {
            base.OnLoaded(client);
            if (client is ReadWriteDevicesUdpClientBase readWriteDevicesUdpClientBase)
            {
                ReadWriteDevicesUdpClientBase = readWriteDevicesUdpClientBase;
                Load();
                ReadWriteDeviceConfig();
            }
            else
            {
                throw new Exception("此适配器只适用于已实现" + nameof(ReadWriteDevicesUdpClientBase) + "的对象使用");
            }
        }
        /// <summary>
        /// 发送前，对当前的命令进行打包处理<br />
        /// </summary>
        public abstract byte[] PackCommandWithHeader(byte[] command);


        /// <summary>
        /// 预发送方法，会对命令重新打包并发送字节数组
        /// </summary>
        /// <param name="item"></param>
        /// <param name="isAsync"></param>
        protected void GoSend(byte[] item, bool isAsync)
        {
            var bytes = PackCommandWithHeader(item);
            NetMessage = NetMessage.NewMessage();
            NetMessage.SendBytes = bytes;
            GoSend(ReadWriteDevicesUdpClientBase.RemoteIPHost.EndPoint, bytes, 0, bytes.Length, isAsync);
            ReadWriteDevicesUdpClientBase.LogNet?.Debug(ToString(), ThingsGateway.Foundation.Resources.Resource.Send + " : " + NetMessage.SendBytes.ToHexString(" "));
        }

        /// <summary>
        /// 初始化，第一次加载时触发
        /// </summary>
        protected abstract void Load();

        protected override void PreviewSend(IRequestInfo requestInfo, bool isAsync)
        {
            if (requestInfo is ReadWriteRequest readWriteRequest)
            {
                ReadWriteRequest = readWriteRequest;
                PreviewSend(isAsync);
            }
            else
            {
                ReadWriteRequest = null;
            }

        }
        /// <summary>
        /// 触发时已赋值<see cref="ReadWriteRequest"/>，此方法需实现触发<see cref="GoSend(byte[], bool)"/>
        /// </summary>
        /// <param name="isAsync"></param>
        protected abstract void PreviewSend(bool isAsync);

        /// <summary>
        /// 把配置属性写入读写设备对象
        /// </summary>
        protected virtual void ReadWriteDeviceConfig()
        {
            ReadWriteDevicesUdpClientBase?.SetValue(ReadWriteDevicesEx.ByteConverterProperty, ByteConverter);
            ReadWriteDevicesUdpClientBase?.SetValue(ReadWriteDevicesEx.TimeOutProperty, TimeOut);
            ReadWriteDevicesUdpClientBase?.SetValue(ReadWriteDevicesEx.WordLengthProperty, WordLength);
        }
        /// <summary>
        /// 根据对方返回的报文命令，对命令进行基本的拆包<br />
        /// </summary>
        /// <remarks>
        /// 在实际解包的操作过程中，通常对状态码，错误码等消息进行判断，如果校验不通过，将携带错误消息返回<br />
        /// </remarks>
        /// <param name="send">发送的原始报文数据</param>
        /// <param name="response">设备方反馈的原始报文内容</param>
        /// <returns>返回拆包之后的报文信息，默认不进行任何的拆包操作</returns>
        protected abstract Result<byte[]> UnpackResponseContent(byte[] send, byte[] response);
        protected override void Dispose(bool disposing)
        {
            ReadWriteRequest = null;
            ReadWriteDevicesUdpClientBase = null;
            base.Dispose(disposing);

        }

        protected override void PreviewReceived(EndPoint remoteEndPoint, ByteBlock byteBlock)
        {
            ReadWriteDevicesUdpClientBase.LogNet?.Debug(ToString(), ThingsGateway.Foundation.Resources.Resource.Received + " : " + NetMessage.ReceivedBytes.ToHexString(" "));

            if (NetMessage?.SendBytes == null)
            {
                GoReceived(remoteEndPoint, byteBlock, null);
                return;
            }
            byte[] header = new byte[] { };
            if (NetMessage.ProtocolHeadBytesLength > 0)
            {
                byteBlock.Read(out header, NetMessage.ProtocolHeadBytesLength);
            }
            if (NetMessage.CheckHeadBytesLegal(header))
            {
                if (NetMessage.BodyLength <= 0)
                {
                    NetMessage.BodyLength = byteBlock.Len;
                }
                byteBlock.Read(out byte[] body, NetMessage.BodyLength);
                var bytes = NetMessage.HeadBytes.SpliceArray(body);
                var unpackbytes = UnpackResponseContent(NetMessage.SendBytes, bytes);
                NetMessage.Message = unpackbytes.Message;
                NetMessage.ResultCode = unpackbytes.ResultCode;
                if (unpackbytes.IsSuccess)
                {
                    NetMessage.Content = unpackbytes.Content;
                    NetMessage.ReceivedBytes = bytes;
                    GoReceived(remoteEndPoint, null, NetMessage);
                    return;
                }
                else
                {
                    byteBlock.Pos = byteBlock.Len;
                    NetMessage.ReceivedBytes = byteBlock.ToArray(0, byteBlock.Len);
                    GoReceived(remoteEndPoint, null, NetMessage);
                    return;
                }

            }
        }

        protected override void PreviewSend(EndPoint endPoint, byte[] buffer, int offset, int length, bool isAsync)
        {
        }

        protected override void PreviewSend(EndPoint endPoint, IList<ArraySegment<byte>> transferBytes, bool isAsync)
        {
        }

        protected override void Reset()
        {
        }
    }
}
